﻿/* ============================================================================================= = */
/* Program.cs - MUCH of this file is derived from the windows forms example project                */
/*              provided with Firelight Technology's FMOD SDK                                      */
/*                                                                                                 */
/*              Please give credit to Firelight Technologies.                                      */
/* =============================================================================================== */



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using System.Windows.Controls;



namespace fmod_wpf_test_1
{
   class Program
   {

      static window1 w;


      [STAThread]
      static void Main(string[] args)
      {
         w = new window1();
         Application app = new Application();

         w.Background = new SolidColorBrush(Color.FromArgb(255, 32, 32, 32));

         w.Show();
         app.Run();
      }
   }


   class window1 : Window
   {

      private Button make_button(string s)
      {
         Button b1 = new Button();
         b1.Background = Brushes.Black;
         TextBlock tb = new TextBlock();
         tb.Text = s;
         tb.FontFamily = new FontFamily("Century Gothic");
         tb.FontSize = 20;
         tb.Foreground = Brushes.White;
         b1.Content = tb;

         return b1;
      }


      private Grid make_ui()
      {
         Grid g = new Grid();

         g.ColumnDefinitions.Add(new ColumnDefinition());
         g.ColumnDefinitions.Add(new ColumnDefinition());
         g.ColumnDefinitions.Add(new ColumnDefinition());
         g.ColumnDefinitions.Add(new ColumnDefinition());

         Button b1 = make_button("Open");
         b1.Click += new RoutedEventHandler(b1_Click);
         Grid.SetColumn(b1, 0);
         g.Children.Add(b1);

         Button play_button = make_button("Play");
         play_button.Click += new RoutedEventHandler(play_button_Click);
         Grid.SetColumn(play_button, 1);
         g.Children.Add(play_button);

         Button pause_button = make_button("Pause");
         pause_button.Click += new RoutedEventHandler(pause_button_Click);
         Grid.SetColumn(pause_button, 2);
         g.Children.Add(pause_button);

         Button exit_button = make_button("Exit");
         exit_button.Click += new RoutedEventHandler(exit_button_Click);
         Grid.SetColumn(exit_button, 3);
         g.Children.Add(exit_button);

         return g;
      }



      private FMOD.System system = null;
      private FMOD.Sound sound = null;
      private FMOD.Channel channel = null;


      spectrum_image si;
      spectrum_image si2;

      waveform_image wi;
      waveform_image wi2;

      spectrum_history_image shi;
      spectrum_history_image shi2;

      const int WB_WIDTH = 128;
      const int WB_HEIGHT = 512;


      const int HISTORY_WIDTH = 128;
      const int HISTORY_HEIGHT = 512;

      DockPanel dp;


      Grid button_grid;
      Grid image_grid;
      Grid master_grid;


      public window1()
      {

         uint version = 0;
         FMOD.RESULT result;

         result = FMOD.Factory.System_Create(ref system);
         ERRCHECK(result);

         result = system.getVersion(ref version);
         ERRCHECK(result);
         if (version < FMOD.VERSION.number) {
            MessageBox.Show("Error!  You are using an old version of FMOD " + version.ToString("X") + ".  This program requires " + FMOD.VERSION.number.ToString("X") + ".");
            Application.Current.Shutdown();
         }

         result = system.init(32, FMOD.INITFLAG.NORMAL, (IntPtr)null);
         ERRCHECK(result);


         //dp = new DockPanel();
         //dp.HorizontalAlignment = HorizontalAlignment.Left;
         //dp.VerticalAlignment = VerticalAlignment.Top;
         //dp.Background = Brushes.Yellow;

         //this.Content = dp;

         this.Title = "WPF WB Demo -- Audio via Firelight Technologies' FMOD Ex";


         button_grid = make_ui();

         image_grid = new Grid();
         image_grid.ColumnDefinitions.Add(new ColumnDefinition());
         image_grid.ColumnDefinitions.Add(new ColumnDefinition());
         image_grid.ColumnDefinitions.Add(new ColumnDefinition());
         image_grid.RowDefinitions.Add(new RowDefinition());
         image_grid.RowDefinitions.Add(new RowDefinition());
         image_grid.RowDefinitions.Add(new RowDefinition());
         image_grid.RowDefinitions.Add(new RowDefinition());
         image_grid.RowDefinitions.Add(new RowDefinition());

         image_grid.ColumnDefinitions[1].Width = new GridLength(1);
         image_grid.RowDefinitions[1].Height = new GridLength(1);
         image_grid.RowDefinitions[3].Height = new GridLength(1);



         si = new spectrum_image(WB_WIDTH, WB_HEIGHT);
         si2 = new spectrum_image(WB_WIDTH, WB_HEIGHT);

         wi = new waveform_image(WB_WIDTH, WB_HEIGHT);
         wi2 = new waveform_image(WB_WIDTH, WB_HEIGHT);

         shi = new spectrum_history_image(HISTORY_WIDTH, HISTORY_HEIGHT);
         shi2 = new spectrum_history_image(HISTORY_WIDTH, HISTORY_HEIGHT);

         Grid.SetColumn(si, 0);
         Grid.SetColumn(si2, 2);

         Grid.SetColumn(wi, 0);
         Grid.SetColumn(wi2, 2);

         Grid.SetColumn(shi, 0);
         Grid.SetColumn(shi2, 2);

         Grid.SetRow(si, 0);
         Grid.SetRow(si2, 0);

         Grid.SetRow(wi, 2);
         Grid.SetRow(wi2, 2);

         Grid.SetRow(shi, 4);
         Grid.SetRow(shi2, 4);



         //DockPanel.SetDock(si, Dock.Bottom);
         //DockPanel.SetDock(si2, Dock.Bottom);

         //DockPanel.SetDock(wi, Dock.Bottom);
         //DockPanel.SetDock(wi2, Dock.Bottom);

         //DockPanel.SetDock(shi, Dock.Bottom);
         //DockPanel.SetDock(shi2, Dock.Bottom);

         si.LayoutTransform = new RotateTransform(-90);
         si2.LayoutTransform = new RotateTransform(-90);

         wi.LayoutTransform = new RotateTransform(-90);
         wi2.LayoutTransform = new RotateTransform(-90);

         shi.LayoutTransform = new RotateTransform(-90);
         shi2.LayoutTransform = new RotateTransform(-90);


         image_grid.Children.Add(si);
         image_grid.Children.Add(si2);

         image_grid.Children.Add(wi);
         image_grid.Children.Add(wi2);

         image_grid.Children.Add(shi);
         image_grid.Children.Add(shi2);


         master_grid = new Grid();
         master_grid.RowDefinitions.Add(new RowDefinition());
         master_grid.RowDefinitions.Add(new RowDefinition());

         Grid.SetRow(image_grid, 0);
         Grid.SetRow(button_grid, 1);

         master_grid.Children.Add(image_grid);
         master_grid.Children.Add(button_grid);

  //       button_grid.Height = 100;
  //       master_grid.

   //      master_grid.RowDefinitions[0].Height = new GridLength(  20, GridUnitType.Star);
         master_grid.RowDefinitions[1].Height = new GridLength(40);//, GridUnitType.Star);


         this.Content = master_grid;

         this.MouseWheel += new System.Windows.Input.MouseWheelEventHandler(window1_MouseWheel);
         this.KeyDown += new System.Windows.Input.KeyEventHandler(window1_KeyDown);

         CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
      }

      void window1_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
      {

         switch (e.Key) {
            case System.Windows.Input.Key.B:
               this.Background = new SolidColorBrush(Color.FromArgb(255, 32, 32, 32));
               break;
            case System.Windows.Input.Key.N:
               MediaElement me = new MediaElement();
               me.Source = new Uri("c:\\Speed_360.wmv");
               me.LoadedBehavior = MediaState.Manual;
               me.Play();
               VisualBrush vb = new VisualBrush(me);
               RenderOptions.SetCachingHint(vb, CachingHint.Cache);
               this.Background = vb;
               break;
            case System.Windows.Input.Key.M:
               RadialGradientBrush rgb = new RadialGradientBrush(Colors.Black, Colors.Yellow);
               RenderOptions.SetCachingHint(rgb, CachingHint.Cache);
               this.Background = rgb;
               break;

            default:
               break;



         }


      }

      void window1_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
      {
			Matrix m = image_grid.RenderTransform.Value;

			if (e.Delta > 0) {
            m.ScaleAt(1.5, 1.5, e.GetPosition(this).X, e.GetPosition(this).Y);
			}
			else {
            m.ScaleAt(1.0 / 1.5, 1.0 / 1.5, e.GetPosition(this).X, e.GetPosition(this).Y);
			}

         image_grid.RenderTransform = new MatrixTransform(m);
      }


      void CompositionTarget_Rendering(object sender, EventArgs e)
      {


         if ((system == null) || channel == null) {
            return;
         }

         system.update();

         bool isplaying = false;
         channel.isPlaying(ref isplaying);

         if (!isplaying) {
            return;
         }


         int channel_count = 0;
         int dummy = 0;
         FMOD.SOUND_FORMAT dummyformat = FMOD.SOUND_FORMAT.NONE;
         FMOD.DSP_RESAMPLER dummyresampler = FMOD.DSP_RESAMPLER.LINEAR;


         system.getSoftwareFormat(ref dummy, ref dummyformat, ref channel_count, ref dummy, ref dummyresampler, ref dummy);


         float[] spectrum = new float[WB_HEIGHT];

         system.getSpectrum(spectrum, WB_HEIGHT, 0, FMOD.DSP_FFT_WINDOW.TRIANGLE);

 //        spectrum[0] = 0
         // HACK
         //for (int n = 0; n < 15; ++n) {
         //   spectrum[n] /= (15 - n);
         //}

         si.update(spectrum);


         system.getSpectrum(spectrum, WB_HEIGHT, 1, FMOD.DSP_FFT_WINDOW.TRIANGLE);

  //       spectrum[0] = 0;
         // HACK
         //for (int n = 0; n < 15; ++n) {
         //   spectrum[n] /= (15 - n);
         //}

         si2.update(spectrum);


         float[] wavedata = new float[WB_HEIGHT];

         system.getWaveData(wavedata, WB_HEIGHT, 0);
         wi.update(wavedata);

         system.getWaveData(wavedata, WB_HEIGHT, 1);
         wi2.update(wavedata);



         float[] spectrum_history = new float[HISTORY_WIDTH];// FIX THIS

         system.getSpectrum(spectrum_history, HISTORY_WIDTH, 0, FMOD.DSP_FFT_WINDOW.TRIANGLE);

  //       spectrum_history[0] = 0;
         // HACK
         //for (int n = 0; n < 15; ++n) {
         //   spectrum_history[n] /= (15 - n);
         //}

         shi.update(spectrum_history);

         system.getSpectrum(spectrum_history, HISTORY_WIDTH, 1, FMOD.DSP_FFT_WINDOW.TRIANGLE);

   //      spectrum_history[0] = 0;
         // HACK
         //for (int n = 0; n < 15; ++n) {
         //   spectrum_history[n] /= (15 - n);
         //}

         shi2.update(spectrum_history);


      }




      class spectrum_history_image : Image
      {
         private WriteableBitmap wb;

         private int pixel_width, pixel_height;

         public spectrum_history_image(int pixel_width, int pixel_height)
         {

            this.pixel_width = pixel_width;
            this.pixel_height = pixel_height;


            wb = new WriteableBitmap(pixel_width, pixel_height, 96, 96, PixelFormats.Pbgra32, null);
            Source = wb;
            RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor);
            //this.HorizontalAlignment = HorizontalAlignment.Stretch;
            //this.VerticalAlignment = VerticalAlignment.Stretch;
            this.ClipToBounds = false;

            this.Stretch = Stretch.Fill;
            this.SnapsToDevicePixels = true;

            RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);

         }


         int active_row = 0;


         public void update(float[] spectrum)
         {
            // find the max amplitude in this channel, to normalize the graph
            float max_amplitude_in_channel = 0;

            int count = spectrum.Count<float>();

            for (int index = 0; index < count; index++) {
               if (max_amplitude_in_channel < spectrum[index]) {
                  max_amplitude_in_channel = spectrum[index];
               }
            }

            wb.Lock();

            IntPtr back_buffer = wb.BackBuffer;

            unsafe {

               int buffer = (int)back_buffer;
               buffer += (active_row * wb.BackBufferStride);
               int* active_row_pointer = (int*)buffer;

               for (int sample_index = 0; sample_index < count; sample_index++) {

                  double normalized_amplitude = spectrum[sample_index] / max_amplitude_in_channel;

                  double r, g, b;

                  if (normalized_amplitude < 0.5) {   // 0 .. 0.5
                     g = 2.0 * (0.5 - normalized_amplitude);
                     b = 1.0 - g;
                     r = 0.0;
                  }
                  else { // 0.5 .. 1.0
                     normalized_amplitude -= 0.5;  // dividing by 2 wouldn't work because 0.5 becomes .25, not 0
                     r = 2.0 * normalized_amplitude;
                     b = 1.0 - r;
                     g = 0;
                  }

                  byte red = (byte)(255 * r);
                  byte green = (byte)((128 * b) + (127 * r));
                  byte blue = (byte)(255 * b);

         //         byte color = (byte)(255 * normalized_amplitude);


                  // bgra32

                  int value = 127 + (red >> 1);
                  value <<= 8;
                  value |= red;
                  value <<= 8;
                  value |= green;
                  value <<= 8;
                  value |= blue;

                  //int value = 255;
                  //value <<= 8;
                  //value |= color;
                  //value <<= 8;
                  //value |= color;
                  //value <<= 8;
                  //value |= color;


                  *active_row_pointer++ = value;
               }
            }

            wb.AddDirtyRect(new Int32Rect(0, active_row, this.pixel_width, 1));
            wb.Unlock();

            ++active_row;
            active_row = active_row % this.pixel_height;
         }

      }






      class waveform_image : Image
      {
         private WriteableBitmap wb;

         private int pixel_width, pixel_height;

         public waveform_image(int pixel_width, int pixel_height)
         {
            this.pixel_width = pixel_width;
            this.pixel_height = pixel_height;

            wb = new WriteableBitmap(pixel_width, pixel_height, 96, 96, PixelFormats.Pbgra32, null);
            Source = wb;
            RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor);
            this.ClipToBounds = false;


            //this.HorizontalAlignment = HorizontalAlignment.Stretch;
            //this.VerticalAlignment = VerticalAlignment.Stretch;
                        this.Stretch = Stretch.Fill;
                        this.SnapsToDevicePixels = true;

                        RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);

         }


         public void update(float[] waveform)
         {
            wb.Lock();
            IntPtr back_buffer = wb.BackBuffer;

            int count = waveform.Count<float>();

            unsafe {

               int* buffer = (int*)back_buffer;

               for (int sample_index = 0; sample_index < count; sample_index++) {

                  int column_to_paint = (int)(((waveform[sample_index] + 1.0) / 2.0) * (this.pixel_width - 1));

                  for (int n = 0; n < column_to_paint; ++n) {
                     *buffer++ = 128 << 24;
                  }

                  unchecked {
                     *buffer++ = (int)0XFFFFFFFF;
                  }

                  for (int n = column_to_paint + 1; n < this.pixel_width; ++n) {
                     *buffer++ = 32 << 24;
                  }
               }
            }

            wb.AddDirtyRect(new Int32Rect(0, 0, this.pixel_width, this.pixel_height));
            wb.Unlock();

         }

      }







      class spectrum_image : Image
      {
         private WriteableBitmap wb;

         private int pixel_width, pixel_height;

         public spectrum_image(int pixel_width, int pixel_height)
         {
            this.pixel_width = pixel_width;
            this.pixel_height = pixel_height;

            wb = new WriteableBitmap(pixel_width, pixel_height, 96, 96, PixelFormats.Pbgra32, null);
            Source = wb;
            RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor);
            //this.HorizontalAlignment = HorizontalAlignment.Stretch;
            //this.VerticalAlignment = VerticalAlignment.Stretch;
                        this.Stretch = Stretch.Fill;

                        this.SnapsToDevicePixels = true;
                        this.ClipToBounds = false;

                        RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);


         }


         public void update(float[] spectrum)
         {
            // find the max amplitude in this channel, to normalize the graph
            float max_amplitude_in_channel = 0;

            int count = spectrum.Count<float>();

            for (int index = 0; index < count; index++) {
               if (max_amplitude_in_channel < spectrum[index]) {
                  max_amplitude_in_channel = spectrum[index];
               }
            }

            wb.Lock();

            IntPtr back_buffer = wb.BackBuffer;

            unsafe {

               int* buffer = (int*)back_buffer;

               for (int sample_index = 0; sample_index < count; sample_index++) {

                  double normalized_height = spectrum[sample_index] / max_amplitude_in_channel;
                  int pixel_height = (int)(normalized_height * (wb.PixelWidth - 1));

                  double r, g, b;

                  if (normalized_height < 0.5) {   // 0 .. 0.5
                     g = 2.0 * (0.5 - normalized_height);
                     b = 1.0 - g;
                     r = 0.0;
                  }
                  else { // 0.5 .. 1.0
                     normalized_height -= 0.5;  // dividing by 2 wouldn't work because 0.5 becomes .25, not 0
                     r = 2.0 * normalized_height;
                     b = 1.0 - r;
                     g = 0;
                  }

                  byte red = (byte)(255 * r);
                  byte green = (byte)(255 * g);
                  byte blue = (byte)(255 * b);

                  // bgra32

                  int value = 255;
                  value <<= 8;
                  value |= red;
                  value <<= 8;
                  value |= green;
                  value <<= 8;
                  value |= blue;

                  for (int n = 0; n < pixel_height; ++n) {
                     *buffer++ = value;
                  }

                  for (int n = pixel_height; n < this.pixel_width; ++n) {
                     *buffer++ = 128 << 24;
                  }
               }
            }

            wb.AddDirtyRect(new Int32Rect(0, 0, this.pixel_width, this.pixel_height));
            wb.Unlock();

         }

      }











      protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
      {


         FMOD.RESULT result;

         /*
             Shut down
         */
         if (sound != null) {
            result = sound.release();
            ERRCHECK(result);
         }

         if (system != null) {
            result = system.close();
            ERRCHECK(result);
            result = system.release();
            ERRCHECK(result);
         }

         //if (components != null) 
         //{
         //   components.Dispose();
         //}

         base.OnClosing(e);
      }



      void b1_Click(object sender, RoutedEventArgs e)
      {

         OpenFileDialog dialog = new OpenFileDialog();



         if (dialog.ShowDialog() == true) // assume true means "OK" ???
         {

            if (sound != null) {
               if (channel != null) {
                  channel.stop();
                  channel = null;
               }
               sound.release();
               sound = null;
            }




            FMOD.RESULT result;

            result = system.createStream(dialog.FileName, FMOD.MODE.SOFTWARE | FMOD.MODE._2D, ref sound);
            ERRCHECK(result);

            //         textBox.Text = dialog.FileName;
         }
      }




      void play_button_Click(object sender, RoutedEventArgs e)
      {
         FMOD.RESULT result;
         bool isplaying = false;

         if (channel != null) {
            channel.isPlaying(ref isplaying);
         }

         if (sound != null && !isplaying) {
            result = system.playSound(FMOD.CHANNELINDEX.FREE, sound, false, ref channel);
            ERRCHECK(result);

            //            playButton.Text = "Stop";
         }
         else {
            if (channel != null) {
               channel.stop();
               channel = null;
            }

            //              playButton.Text = "Play";
         }
      }





      void pause_button_Click(object sender, RoutedEventArgs e)
      {
         if (channel != null) {
            bool ispaused = false;

            channel.getPaused(ref ispaused);
            channel.setPaused(!ispaused);

            if (ispaused) {
               //                 pauseButton.Text = "Pause";
            }
            else {
               //                 pauseButton.Text = "Resume";
            }
         }
      }




      void exit_button_Click(object sender, RoutedEventArgs e)
      {
         Application.Current.Shutdown();
      }



      private void ERRCHECK(FMOD.RESULT result)
      {
         if (result != FMOD.RESULT.OK) {
            MessageBox.Show("FMOD error! " + result + " - " + FMOD.Error.String(result));
            Environment.Exit(-1);
         }
      }




   }



}


